from sqlalchemy.ext.compiler import compiles
import pandas as pd
import time
import platform
import json
import requests
import uuid
from urllib.parse import quote_plus
from datetime import datetime
import re
import traceback
from sqlalchemy import create_engine, Table, MetaData
from sqlalchemy.sql.expression import Insert
from sqlalchemy.dialects.mysql import insert
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine, text
import time 
from urllib.parse import quote_plus
class MySQLHelper:
    def __init__(self):
        if platform.system() == 'Windows':  
            self.host = '106.53.97.117'
            self.port = 3306
            self.user = 'my_user'
            self.password = quote_plus('my_password') 
            self.db = 'my_database'
            self.charset = "utf8mb4"
            self.engine = None
        else:
            from app.config import Config
            self.host = Config.DATABASE_HOST
            self.port = Config.DATABASE_PORT
            self.user = Config.DATABASE_USER
            self.password = quote_plus(Config.DATABASE_PASSWORD) 
            self.db = Config.DATABASE_DATABASE
            self.charset = "utf8mb4"
            self.engine = None    
        self.Session = None
        self._create_engine()

    def _create_engine(self):
        # 创建数据库连接引擎，使用连接池
        # 检查端口是否为空，如果为空则使用默认端口3306
        port = self.port if self.port else "3306"
        # 使用与配置文件一致的连接器，默认使用mysqlconnector
        connection_string = f"mysql+mysqlconnector://{self.user}:{self.password}@{self.host}:{port}/{self.db}?charset={self.charset}"
        self.engine = create_engine(connection_string, pool_size=10, max_overflow=1490, pool_recycle=3600)
        self.Session = sessionmaker(bind=self.engine)

    def get_session(self):
        # 获取数据库会话
        return self.Session()

    def mysql_replace_into(self, table, conn, keys, data_iter):
        # 定义一个编译器扩展，用于将 INSERT INTO 替换为 REPLACE INTO
        @compiles(Insert)
        def replace_string(insert, compiler, **kw):
            s = compiler.visit_insert(insert, **kw)
            s = s.replace("INSERT INTO", "REPLACE INTO")
            return s

        # 准备数据
        data = [dict(zip(keys, row)) for row in data_iter]
        
        # 执行插入操作
        conn.execute(table.table.insert(), data)

    def _df_res_to_db(self, pd, table_name):
        pd.to_sql(
            name=table_name,
            con=self.engine.connect(),
            index=False,
            chunksize=2000,
            if_exists='append', method=self.mysql_replace_into)
            
    def execute_select(self, sql):
        time_start = time.time()
        with self.engine.connect() as conn:
            # 使用 text() 函数将 SQL 语句转换为可执行对象
            sql_text = text(sql)
            result = conn.execute(sql_text)
            data = []
            for row in result:
                # 使用 row._mapping 将 RowProxy 转换为字典
                data.append(dict(row._mapping))
            time_end = time.time()
            # print(f"执行时间：{time_end - time_start}")
            return data

    def execute_value(self, sql):
        with self.engine.connect() as conn:
            result = conn.execute(sql)
            return result.fetchone()

    def execute_edit(self, sql):
        with self.engine.connect() as conn:
            result = conn.execute(sql)
            conn.commit()
            return result.rowcount
            
    def execute_charts(self, sql):
        """
        :return 图表数据
        """
        with self.engine.connect() as conn:
            result = conn.execute(sql)
            target = {}
            for j in range(len(result.keys())):
                data = [row[j] for row in result]
                target[result.keys()[j]] = data
            return target

    def execute_datatable(self, sql):
        with self.engine.connect() as conn:
            result = conn.execute(sql)
            datas = []
            for row in result:
                target = list(row)
                datas.append(target)
            data = {"data": datas}
            return data

    def execute_cloud(self, sql):
        with self.engine.connect() as conn:
            result = conn.execute(sql)
            data = {row[0]: row[1] for row in result}
            return data
            
    def get_exhibition_by_id(self, exhibition_id):
        """
        根据ID获取展会信息
        """
        if not exhibition_id:
            return None
            
        sql = f"""
        SELECT 
            chinese_name,english_name,
    english_short_name,
    holding_time,
    event_date_start,
    event_date_end,
    event_month,
    continent,
    country,
    city,
    frequency,
    industry,
    organizer,
    organizer_en,
    contact_info,
    exhibition_hall,exhibition_hall_english,
    official_website,
    exhibit_scope,
    description,
    ufi_certified,
    exhibit_scope,
    '小曾' as soource_user,
    ai_link,
    ai_link_time,
    exhibition_area
        FROM exhibitions 
        WHERE id = {exhibition_id} and length(english_name)<2
        LIMIT 1
        """
        
        result = self.execute_select(sql)
        if result and len(result) > 0:
            return result[0]
        return None
        
    def update_exhibition(self, exhibition_id, update_data):
        """
        更新展会信息
        
        Args:
            exhibition_id: 展会ID
            update_data: 需要更新的字段和值的字典
        
        Returns:
            布尔值，表示更新是否成功
        """
        if not exhibition_id or not update_data:
            print("更新数据为空，不执行更新")
            return False
        
        # 构建SET部分的SQL
        set_clauses = []
        for field, value in update_data.items():
            if isinstance(value, str):
                # 字符串需要转义并加引号
                value = value.replace("'", "''")
                set_clauses.append(f"{field} = '{value}'")
            elif value is None:
                set_clauses.append(f"{field} = NULL")
            else:
                set_clauses.append(f"{field} = {value}")
                
        if not set_clauses:
            print("SET子句为空，不执行更新")
            return False
        
        set_sql = ", ".join(set_clauses)
        
        sql = f"""
        UPDATE exhibitions 
        SET {set_sql}
        WHERE id = {exhibition_id}
        """
        
        try:
            # 使用事务执行更新
            with self.engine.connect() as conn:
                with conn.begin():
                    try:
                        result = conn.execute(text(sql))
                        rows_affected = result.rowcount
                        print(f"数据库更新结果: {rows_affected} 行受影响")
                        return rows_affected > 0
                    except Exception as e:
                        print(f"执行SQL时出错: {str(e)}")
                        print(f"SQL语句: {sql}")
                        return False
        except Exception as e:
            print(f"数据库连接出错: {str(e)}")
            # 尝试重新创建连接
            try:
                print("尝试重新创建数据库连接...")
                self._create_engine()
                with self.engine.connect() as conn:
                    with conn.begin():
                        result = conn.execute(text(sql))
                        rows_affected = result.rowcount
                        print(f"重连后数据库更新结果: {rows_affected} 行受影响")
                        return rows_affected > 0
            except Exception as reconnect_error:
                print(f"重新连接数据库失败: {str(reconnect_error)}")
                return False

    def update_ai_status(self, exhibition_id, status):
        """
        更新展会的AI补全状态
        
        Args:
            exhibition_id: 展会ID
            status: 状态描述
        """
        if not exhibition_id:
            return False
            
        # 处理状态文本，防止SQL注入
        if isinstance(status, str):
            status = status.replace("'", "''")
            
        sql = f"""
        UPDATE exhibitions 
        SET ai_checked_status = 
            CASE 
                WHEN '{status}' = 'AI补全中' THEN 4
                WHEN '{status}' = 'AI补全成功' THEN 5
                WHEN '{status}' LIKE 'AI补全失败%' THEN 2
                ELSE 3
            END,
            ai_check_description = '{status}'
        WHERE id = {exhibition_id}
        """
        
        # 使用事务执行更新
        with self.engine.connect() as conn:
            with conn.begin():
                result = conn.execute(text(sql))
                return result.rowcount > 0


def create_exhibition_info(link, exhibition_row, mode="chat"):
    """
    使用API为展会链接创建补全信息
    
    Args:
        link: 展会链接
        exhibition_row: 展会记录
        mode: 模式，link或chat
    
    Returns:
        字典，包含success标志和结果或错误信息
    """
    try:
        # 使用相同的API密钥
        API_KEY = "fastgpt-kspcWF3r4XnI9leHXcZqE6tkY2LKGx7p9WgT3fMD9Ywtn06zyQ36oFdo4Vb"
        
        # 根据模式设置不同的URL和payload
        if mode == "link":
            # 创建link集合
            collection_result = create_collection(link)
            if not collection_result.get('success'):
                return collection_result
                
            collection_id = collection_result.get('collectionId')
            
            # 使用collection进行对话
            url = "http://106.53.97.117:3000/api/v1/chat/completions"
            
            headers = {
                "Accept": "application/json, text/plain, */*",
                "Content-Type": "application/json",
                "Authorization": f"Bearer {API_KEY}"
            }
            
            prompt = f"""
以下是报表，请你根据知识库查询，对该展览会的其余信息进行补全：
{exhibition_row}
        仅仅返回给你的json格式数据（字段没说要英文的就填写中文,exhibition_number无需改)"""
            
            payload = {
                "chatId": "tHnNXlkjUr7E6cX7w7UylJtb",  # 使用固定的chatId
                "appId": "68132f43b037de87a46e6e54",  # 使用正确的appId
                "stream": True,  # 使用流式请求
                "detail": False,
                "variables": {},
                "messages": [
                    {
                        "content": prompt,
                        "role": "user"
                    }
                ],
                "modules": [
                    {
                        "moduleId": "collection",
                        "collectionId": collection_id
                    }
                ]
            }
            
        else:  # mode == "chat"
            url = "http://106.53.97.117:3000/api/v1/chat/completions"
            
            headers = {
                "Accept": "text/event-stream",  # 使用事件流格式
                "Content-Type": "application/json",
                "Authorization": f"Bearer {API_KEY}"
            }
            prompt = f"""
以下是报表，请你根据知识库查询，对该展览会的其余信息进行补全 如果不是2025年展会 就不要乱写 或者不要写2026年的：
{exhibition_row}
        仅仅返回给你的json格式数据（字段没说要英文的就填写中文,exhibition_number无需改)"""
            
            # 生成唯一的chatId和responseChatItemId
            chat_id = "tHnNXlkjUr7E6cX7w7UylJta"
            response_chat_item_id = str(uuid.uuid4()).replace("-", "")
            
            payload = {
                "chatId": chat_id,
                "responseChatItemId": response_chat_item_id,
                "stream": True,  # 使用流式请求
                "detail": False,
                "appId": "68132f43b037de87a46e6e53",  # 使用正确的appId
                "messages": [
                    {
                        "role": "user",
                        "content": prompt
                    }
                ],
                "variables": {
                    "cTime": time.strftime("%Y-%m-%d %H:%M:%S %A")
                }
            }
        
        # 发送请求并处理响应
        print(f"准备发送API请求: {url}")
        print(f"请求参数: {json.dumps(payload, ensure_ascii=False)[:500]}...")
        
        # 添加最大重试次数和重试延迟
        max_retries = 3
        retry_delay = 2  # 秒
        last_exception = None
        
        for retry_count in range(max_retries):
            try:
                # 增加超时设置，避免长时间等待
                response = requests.post(
                    url, 
                    json=payload, 
                    headers=headers, 
                    timeout=(10, 120),  # 连接超时10秒，读取超时120秒 (缩短超时时间避免长时间阻塞)
                    stream=(mode == "chat")  # 对于chat模式，使用流式传输
                )
                print(f"API响应状态码: {response.status_code}")
                
                # 如果响应状态不是200，引发异常
                response.raise_for_status()
                
                # 成功请求，跳出重试循环
                break
                
            except requests.exceptions.RequestException as e:
                last_exception = e
                print(f"API请求失败 (尝试 {retry_count+1}/{max_retries}): {str(e)}")
                
                # 如果不是最后一次尝试，则等待后重试
                if retry_count < max_retries - 1:
                    wait_time = retry_delay * (2 ** retry_count)  # 指数退避
                    print(f"等待 {wait_time} 秒后重试...")
                    time.sleep(wait_time)
                else:
                    print(f"达到最大重试次数 ({max_retries})，放弃请求")
                    # 最后一次尝试也失败，返回错误
                    return {'success': False, 'error': f'请求失败: {str(e)}'}
        
        # 处理响应数据
        if mode == "chat" or mode == "link":  # 现在两种模式都使用流式响应
            # 处理流式响应
            full_response = ""
            try:
                for line in response.iter_lines():
                    if line:
                        # 移除 "data: " 前缀并解析 JSON
                        if line.startswith(b'data: '):
                            json_str = line[6:].decode('utf-8')
                            
                            # 处理完成标记
                            if json_str.strip() == '[DONE]':
                                print("收到完成标记")
                                break
                                
                            try:
                                data = json.loads(json_str)
                                # 提取内容
                                if 'choices' in data and len(data['choices']) > 0:
                                    delta = data['choices'][0].get('delta', {})
                                    content = delta.get('content', '')
                                    if content:
                                        print(content, end='', flush=True)
                                        full_response += content
                            except json.JSONDecodeError:
                                # 如果无法解析JSON，跳过
                                continue
                
                # 验证响应是否为空
                if not full_response.strip():
                    print("警告: 收集到的完整响应为空")
                    return {'success': False, 'error': '收集到的响应为空'}
                    
                print(f"\n完整响应: {full_response[:200]}...")
                
                # 尝试从响应文本中提取JSON
                json_match = re.search(r'({[\s\S]*})', full_response)
                if json_match:
                    try:
                        ai_data = json.loads(json_match.group(1))
                        return {'success': True, 'data': ai_data}
                    except json.JSONDecodeError as e:
                        print(f"解析流式响应JSON失败: {str(e)}")
                        return {'success': False, 'error': f'解析流式响应JSON失败: {str(e)}', 'content': full_response}
                else:
                    return {'success': False, 'error': '无法在流式响应中找到JSON数据', 'content': full_response}
                    
            except Exception as e:
                print(f"处理流式响应时出错: {str(e)}")
                return {'success': False, 'error': f'处理流式响应出错: {str(e)}'}
        else:
            # 处理普通响应（link模式）
            try:
                # 解析JSON响应
                response_data = response.json()
                print(f"响应内容前200个字符: {json.dumps(response_data, ensure_ascii=False)[:200]}...")
                
                if 'data' not in response_data or 'content' not in response_data['data']:
                    print(f"API响应缺少预期字段: {json.dumps(response_data, ensure_ascii=False)}")
                    return {'success': False, 'error': '响应缺少预期字段', 'response': response_data}
                
                # 提取内容部分
                content = response_data['data']['content']
                
                # 确保内容不为空
                if not content:
                    print("API返回的内容为空")
                    return {'success': False, 'error': 'API返回内容为空', 'response': response_data}
                
                print(f"提取的内容: {content[:200]}...")  # 打印前200个字符
                
                # 尝试从内容中解析JSON
                # 尝试匹配JSON对象样式的内容
                json_match = re.search(r'({[\s\S]*})', content)
                if json_match:
                    try:
                        ai_data = json.loads(json_match.group(1))
                        return {'success': True, 'data': ai_data}
                    except json.JSONDecodeError as e:
                        print(f"解析JSON失败: {str(e)}")
                        return {'success': False, 'error': f'解析JSON失败: {str(e)}', 'content': content}
                else:
                    print("无法在内容中找到JSON")
                    return {'success': False, 'error': '无法在内容中找到JSON', 'content': content}
            except json.JSONDecodeError as e:
                print(f"解析响应JSON失败: {str(e)}")
                return {'success': False, 'error': f'解析响应JSON失败: {str(e)}'}
    
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {str(e)}")
        return {'success': False, 'error': f'请求失败: {str(e)}'}
    except json.JSONDecodeError as e:
        print(f"解析响应JSON失败: {str(e)}")
        return {'success': False, 'error': f'解析响应JSON失败: {str(e)}'}
    except Exception as e:
        print(f"处理过程中发生未预期的错误: {str(e)}")
        print(traceback.format_exc())
        return {'success': False, 'error': f'未预期的错误: {str(e)}'}



db = MySQLHelper()

def format_url(url):
    if not url:
        return None
    
    # Remove any whitespace
    url = url.strip()
    
    if not url:
        return None
    
    # Remove any existing http:// or https:// prefix
    url = re.sub(r'^https?://', '', url)
    
    # Create different URL variations
    url_variations = []
    
    # Original domain (with www if present)
    url_variations.append(f"http://{url}")
    url_variations.append(f"https://{url}")
    
    # Remove www. if present
    if url.startswith('www.'):
        url_without_www = url[4:]
        url_variations.append(f"http://{url_without_www}")
        url_variations.append(f"https://{url_without_www}")
    else:
        # Add www. if not present
        url_variations.append(f"http://www.{url}")
        url_variations.append(f"https://www.{url}")
    
    return url_variations

def create_collection(link_url):
    if not link_url:
        return {'success': False, 'error': 'No URL provided'}
        
    url = "http://106.53.97.117:3000/api/core/dataset/collection/create/link"
    
    # 使用新的API密钥
    API_KEY = "fastgpt-kspcWF3r4XnI9leHXcZqE6tkY2LKGx7p9WgT3fMD9Ywtn06zyQ36oFdo4Vb"
    
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_KEY}"
    }
    
    # 更新payload格式以匹配curl请求
    print(f"{link_url} - 准备创建链接知识库")
    payload = {
        "link": link_url,
        "datasetId": "67e3b1d27a46e4ccc2879bfd",
        "parentId": None,
        "trainingType": "chunk",
        "chunkSettingMode": "auto",
        "qaPrompt": "",
        "metadata": {
            "webPageSelector": ""
        }
    }
    
    try:
        # 添加最大重试次数和重试延迟
        max_retries = 3
        retry_delay = 2  # 秒
        
        for retry_count in range(max_retries):
            try:
                # 增加超时设置，避免长时间等待
                response = requests.post(
                    url, 
                    json=payload, 
                    headers=headers, 
                    timeout=(10, 30)  # 连接超时10秒，读取超时30秒
                )
                print(f"创建集合API响应状态码: {response.status_code}")
                print(f"响应内容: {response.text[:200]}...")  # 只打印前200个字符避免日志过长
                
                # 检查响应状态码
                response.raise_for_status()
                
                # 成功请求，解析响应
                response_data = response.json()
                collection_id = response_data.get('data', {}).get('collectionId')
                
                if collection_id:
                    print(f"成功创建集合，ID: {collection_id}")
                    return {'success': True, 'collectionId': collection_id}
                else:
                    print("响应中未找到collection_id")
                    if retry_count < max_retries - 1:
                        wait_time = retry_delay * (2 ** retry_count)
                        print(f"等待 {wait_time} 秒后重试...")
                        time.sleep(wait_time)
                        continue
                    return {'success': False, 'error': '未找到 collectionId'}
                
            except requests.exceptions.RequestException as e:
                print(f"创建集合请求失败 (尝试 {retry_count+1}/{max_retries}): {str(e)}")
                
                # 如果不是最后一次尝试，则等待后重试
                if retry_count < max_retries - 1:
                    wait_time = retry_delay * (2 ** retry_count)  # 指数退避
                    print(f"等待 {wait_time} 秒后重试...")
                    time.sleep(wait_time)
                else:
                    print(f"达到最大重试次数 ({max_retries})，放弃请求")
                    return {'success': False, 'error': f'请求失败: {str(e)}'}
                    
            except json.JSONDecodeError as e:
                print(f"解析响应JSON失败 (尝试 {retry_count+1}/{max_retries}): {str(e)}")
                
                # 如果不是最后一次尝试，则等待后重试
                if retry_count < max_retries - 1:
                    wait_time = retry_delay * (2 ** retry_count)
                    print(f"等待 {wait_time} 秒后重试...")
                    time.sleep(wait_time)
                else:
                    print(f"达到最大重试次数 ({max_retries})，放弃请求")
                    return {'success': False, 'error': f'解析响应失败: {str(e)}'}
                    
        # 如果所有重试都失败了（虽然应该不会执行到这里）
        return {'success': False, 'error': '所有重试尝试都失败了'}
    
    except Exception as e:
        print(f"创建集合时发生未预期错误: {str(e)}")
        return {'success': False, 'error': f'未知错误: {str(e)}'}

def try_create_collection(url_variations):
    for url in url_variations:
        print(f"尝试访问: {url}")
        result = create_collection(url)
        if result['success']:
            return {'success': True, 'collectionId': result['collectionId'], 'working_url': url}
        time.sleep(1)  # 在重试之间添加短暂延迟
    return {'success': False, 'error': '所有URL变体都尝试失败', 'last_tried_url': url_variations[-1]}

def process_website_link(website):
    """处理官网链接并返回AI链接相关信息"""
    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    if not website:
        return {
            'ai_link_status': 3,
            'ai_link': '',
            'ai_link_time': current_time
        }
            
    # 获取所有可能的URL格式
    url_variations = format_url(website)
    if not url_variations:
        return {
            'ai_link_status': 2,
            'ai_link': website,
            'ai_link_time': current_time
        }
            
    # 尝试不同的URL格式
    result = try_create_collection(url_variations)
    
    if result['success']:
        return {
            'ai_link_status': 1,
            'ai_link': result['working_url'],
            'ai_link_time': current_time
        }
    else:
        return {
            'ai_link_status': 2,
            'ai_link': result['last_tried_url'],
            'ai_link_time': current_time
        }

def process_exhibition(exhibition_id, website=None):
    """处理单个展会数据的AI补全"""
    try:
        # 初始化MySQL连接和更新AI状态
        mysql = MySQLHelper()
        exhibition_row = mysql.get_exhibition_by_id(exhibition_id)
        
        if not exhibition_row:
            print(f"未找到展会ID {exhibition_id}")
            return _update_ai_status_failed(mysql, exhibition_id, "未找到展会记录")
        
        # 更新为处理中状态
        mysql.update_ai_status(exhibition_id, "AI补全中")
        
        # 获取展会官网 - 优先使用直接传入的website参数，否则使用数据库中的数据
        if website is None:
            website = exhibition_row.get('website')
        
        if not website:
            return _update_ai_status_failed(mysql, exhibition_id, "展会无官网链接")
        
        # 调用AI补全
        print(f"开始处理展会ID {exhibition_id}, 官网: {website}")
        
        try:
            # 首先尝试使用link模式
            result = create_exhibition_info(website, exhibition_row, mode="link")
            
            # 如果link模式失败，则尝试使用chat模式
            if not result.get('success'):
                print(f"Link模式失败，尝试Chat模式: {result.get('error')}")
                result = create_exhibition_info(website, exhibition_row, mode="chat")
            
            # 如果都失败了
            if not result.get('success'):
                error_msg = result.get('error', '未知错误')
                print(f"展会ID {exhibition_id} AI补全失败: {error_msg}")
                return _update_ai_status_failed(mysql, exhibition_id, f"AI补全失败: {error_msg}")
            
            # 成功获取数据
            ai_data = result.get('data', {})
            print(f"AI补全数据: {json.dumps(ai_data, ensure_ascii=False)[:200]}...")
            
            # 定义AI返回数据键与数据库字段的映射
            field_mapping = {
                # AI返回的键 -> 数据库字段
                '展会中文名称': 'chinese_name',
                '展会英文名称': 'english_name',
                '举办国家': 'country',
                '举办城市': 'city',
                '举办场馆': 'exhibition_hall',
                '举办时间': 'holding_time',
                '展会周期': 'frequency',
                '展会届数': 'exhibition_session',
                '主办单位': 'organizer',
                '承办单位': 'undertaker',
                '参展范围': 'exhibit_scope',
                '展会概况': 'description',
                
                # 直接的字段名称匹配
                'chinese_name': 'chinese_name',
                'english_name': 'english_name',
                'english_short_name': 'english_short_name',
                'holding_time': 'holding_time',
                'event_date_start': 'event_date_start',
                'event_date_end': 'event_date_end',
                'event_month': 'event_month',
                'continent': 'continent',
                'country': 'country',
                'city': 'city',
                'frequency': 'frequency',
                'industry': 'industry',
                'organizer': 'organizer',
                'organizer_en': 'organizer_en',
                'contact_info': 'contact_info',
                'exhibition_hall': 'exhibition_hall',
                'exhibition_hall_english': 'exhibition_hall_english',
                'official_website': 'official_website',
                'exhibit_scope': 'exhibit_scope',
                'description': 'description',
                'ufi_certified': 'ufi_certified',
                'exhibition_area': 'exhibition_area'
            }
            
            # 更新数据库
            update_data = {}
            
            # 根据映射处理数据
            for ai_key, db_field in field_mapping.items():
                if ai_key in ai_data and ai_data[ai_key]:
                    value = ai_data[ai_key]
                    # 对于布尔值或数字字符串，尝试转换
                    if isinstance(value, str) and value.isdigit():
                        try:
                            value = int(value)
                        except ValueError:
                            pass
                    update_data[db_field] = value
            
            # 过滤掉空值和None值
            update_data = {k: v for k, v in update_data.items() if v is not None and v != ''}
            
            # 打印详细的更新数据信息
            print(f"更新展会ID {exhibition_id} 字段数: {len(update_data)}")
            print(f"更新展会ID {exhibition_id} 数据: {update_data}")
            
            # 使用事务来确保数据一致性
            if update_data:
                # 最大重试次数
                max_db_retries = 3
                retry_count = 0
                
                while retry_count < max_db_retries:
                    try:
                        # 先更新展会数据
                        print(f"正在更新展会ID {exhibition_id} 的数据（尝试 {retry_count+1}/{max_db_retries}）...")
                        update_success = mysql.update_exhibition(exhibition_id, update_data)
                        
                        if update_success:
                            # 再更新状态
                            print(f"展会ID {exhibition_id} 数据更新成功，更新AI状态...")
                            mysql.update_ai_status(exhibition_id, "AI补全成功")
                            print(f"展会ID {exhibition_id} AI补全成功")
                            return True
                        else:
                            print(f"展会ID {exhibition_id} 数据更新失败，行数为0")
                            retry_count += 1
                            if retry_count < max_db_retries:
                                print(f"等待 2 秒后重试...")
                                time.sleep(2)
                            else:
                                return _update_ai_status_failed(mysql, exhibition_id, "数据更新失败，更新行数为0")
                    
                    except Exception as e:
                        print(f"数据库更新失败: {str(e)}")
                        retry_count += 1
                        if retry_count < max_db_retries:
                            print(f"等待 2 秒后重试...")
                            time.sleep(2)
                        else:
                            return _update_ai_status_failed(mysql, exhibition_id, f"数据库更新失败: {str(e)}")
            else:
                print(f"展会ID {exhibition_id} AI补全没有获取到有效数据")
                return _update_ai_status_failed(mysql, exhibition_id, "AI补全没有获取到有效数据")
                
        except Exception as e:
            print(f"AI补全处理出错: {str(e)}")
            return _update_ai_status_failed(mysql, exhibition_id, f"AI补全处理错误: {str(e)}")
    
    except Exception as e:
        print(f"处理展会ID {exhibition_id} 时发生错误: {str(e)}")
        print(traceback.format_exc())
        try:
            return _update_ai_status_failed(mysql, exhibition_id, f"处理错误: {str(e)[:100]}")
        except:
            print("更新失败状态时也发生错误")
            return False



def _update_ai_status_failed(mysql, exhibition_id, reason):
    """更新展会AI状态为失败"""
    try:
        mysql.update_ai_status(exhibition_id, f"AI补全失败: {reason[:100]}")
        print(f"展会ID {exhibition_id} 标记为AI补全失败: {reason}")
        return False
    except Exception as e:
        print(f"更新展会ID {exhibition_id} AI状态失败: {str(e)}")
        return False

def get_exhibitions_for_ai_completion():
    """
    获取需要AI补全的展会列表
    
    Returns:
        包含展会ID、中文名称和官方网站的记录列表
    """
    mysql = MySQLHelper()
    sql = """
    SELECT id, chinese_name, official_website
    FROM exhibitions 
    WHERE created_at >= CURDATE() AND ai_link_status = 1
    """
    
    try:
        results = mysql.execute_select(sql)
        print(f"找到 {len(results)} 条需要AI补全的展会记录")
        return results
    except Exception as e:
        print(f"查询需要AI补全的展会失败: {str(e)}")
        return []

def main():
    """
    主函数：获取并处理所有需要AI补全的展会
    """
    print("开始批量AI补全展会信息...")
    
    # 获取需要补全的展会列表
    exhibitions = get_exhibitions_for_ai_completion()
    
    if not exhibitions:
        print("没有找到需要AI补全的展会记录")
        return
    
    total = len(exhibitions)
    success_count = 0
    failed_count = 0
    
    # 循环处理每个展会
    for index, exhibition in enumerate(exhibitions, 1):
        try:
            exhibition_id = exhibition.get('id')
            chinese_name = exhibition.get('chinese_name', '')
            official_website = exhibition.get('official_website', '')
            
            print(f"\n正在处理 [{index}/{total}] 展会ID: {exhibition_id}, 名称: {chinese_name}, 官网: {official_website}")
            
            if not official_website:
                print(f"展会ID {exhibition_id} 没有提供官方网站，跳过")
                failed_count += 1
                continue
            
            # 处理展会 - 直接传入official_website，不再需要从数据库重新获取
            result = process_exhibition(exhibition_id, website=official_website)
            
            if result:
                success_count += 1
            else:
                failed_count += 1
                
        except KeyboardInterrupt:
            print("\n用户中断，停止处理")
            break
        except Exception as e:
            failed_count += 1
            print(f"处理展会索引 [{index}/{total}] 时发生未捕获的错误: {str(e)}")
            print(traceback.format_exc())
            continue
    
    # 打印统计信息
    print("\n=====================")
    print(f"AI补全处理完成，总计: {total}, 成功: {success_count}, 失败: {failed_count}")
    print("=====================")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n程序被用户中断")
    except Exception as e:
        print(f"程序运行出错: {str(e)}")
        print(traceback.format_exc())

